#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>

#include "net/netglobal.h"
#include "log.h"
#include "global.h"

// 守护进程初始化函数实现
int Daemon(){
    // 步骤1：fork()一个子进程作为守护进程
    switch (fork())
    {
    case -1:
        Log_Info(errno, "守护进程初始化函数中fork()失败!");
        return -1;
    case 0:
        // 守护进程跳出去执行相应代码
        break;
    default:
        // 父进程（原master进程）直接结束，让守护进程变成孤儿进程，被init进程接管，从而脱离终端控制
        Log_Info(errno, "由于开启守护进程模式，旧master进程主动结束！再见！！");
        exit(0);
    }

    // 能走到这里是守护进程
    g_pid = getpid();// 守护进程自身的pid要重新获取
    g_process = PROCESS_GUARD;// 标记自身为守护进程
    Log_Info(errno, "成功开启守护进程!");

    // 步骤2：创建新会话，脱离终端（守护进程成为独立进程组和会话组长）
    if(setsid() == -1){
        Log_Info(errno,"守护进程初始化中setsid()失败!");
        exit(-1);// 直接终止程序
    }

    // 步骤3：fork()一个子进程作为新的master进程
    pid_t master_pid = fork();
    if (master_pid == -1) {
        Log_Info(errno, "守护进程尝试启动新的master进程时fork()失败!");
        Log_Info(0, "守护进程遗憾下岗！");
        exit(-1);// 都无法创建master进程了，异常退出自己吧
    } 
    else if (master_pid == 0) {
        // 开启新的 master 进程逻辑(此时标记不创建守护进程，避免嵌套)
        execl("net", "program", "--no-daemon", (char*)NULL);
        exit(EXIT_FAILURE); // execl 失败时退出
    } 
    else {
        // 守护进程记录 master 进程的 PID
        g_child_pid = master_pid;
    }

    // 步骤4：重置文件权限掩码，避免默认 umask（如 022）影响新创建文件的权限
    umask(0);

    // 步骤5：重定向标准输入/输出到黑洞设备（写入的数据被丢弃，读取时返回 EOF）
    int fd = open("/dev/null", O_RDWR);
    // 如果打开失败
    if (fd == -1){
        Log_Info(errno,"守护进程初始化中open(\"/dev/null\")失败!");
        exit(-1);// 直接终止程序
    }
    // 重定向标准输入：关闭STDIN_FILENO[这是规矩，已经打开的描述符，动他之前，先close]，类似于指针指向null，让/dev/null成为标准输入
    if (dup2(fd, STDIN_FILENO) == -1){
        Log_Info(errno, "守护进程初始化中dup2(STDIN)失败!");
        exit(-1);// 直接终止程序
    }
    // 重定向标准输出：关闭STDIN_FILENO，类似于指针指向null，让/dev/null成为标准输出；
    if (dup2(fd, STDOUT_FILENO) == -1){
        Log_Info(errno,"守护进程初始化中dup2(STDOUT)失败!");
        exit(-1);// 直接终止程序
    }
    // 关闭冗余文件描述符
    if (fd > STDERR_FILENO){
        // 释放资源这样这个文件描述符就可以被复用
        if (close(fd) == -1){
            Log_Info(errno, "守护进程初始化中close(fd)失败!");
            exit(-1);// 直接终止程序
        }
    }

    // 守护进程进入监控循环
    Daemon_main_loop();

    // 如果退出循环，说明守护进程已经没用了，自己结束自己吧
    exit(0);
}

// 守护进程监控循环（由守护进程调用，守护进程是master进程的子进程）
void Daemon_main_loop(){
    for( ;; ){
        int status;
        // 等待master进程状态变化（非阻塞）
        pid_t ret = waitpid(g_child_pid, &status, WNOHANG);
            
        if(ret == -1){
            Log_Info(errno, "守护进程waitpid()失败，守护进程退出！");
            return;
        }
        // 监控到master进程退出
        else if(ret == g_child_pid){
            if(WIFEXITED(status)){
                Log_Info(0, "守护进程发现master进程正常退出，状态码=%d", WEXITSTATUS(status));
                Log_Info(0, "守护进程光荣下岗！");
                return;

            }else{
                Log_Info(0, "子进程异常退出，信号=%d", WTERMSIG(status));
                // 异常退出尝试重启master进程
                int result = RestartChildProcess();
                if(result == -1){
                    return;// 如果重启失败了，不管什么原因，都结束守护进程吧
                }
            }
        }

        sleep(20);  // 每隔20秒检查一次，避免太频繁
    }
}

// 守护进程监控到master进程异常退出后的重启函数
int RestartChildProcess(){
    pid_t pid = fork();// 新的master进程
    if (pid == -1) {
        Log_Info(errno, "守护进程尝试重启master进程时fork()失败！");
        return -1;
    }
    else if (pid == 0) {
        // 新的master进程
        execl("net", "program","--no-daemon", (char*)NULL);
        return -1;
    }
    else {
        g_child_pid = pid;  // 记录新的需要监控的master进程PID
    }
    return 0;
}